using Kopernikus.Collections.Generic;
using Kopernikus.Parsing.Utility;

namespace Kopernikus.Parsing.GrammarSets
{
    public class FirstSets
    {
        private readonly IGrammar m_grammar;
        private SetMap<Symbol, Symbol> m_firstSets;

        public FirstSets(IGrammar grammar)
        {
            m_grammar = grammar;
        }


        public IGrammar Grammar
        {
            get { return m_grammar; }
        }

        public Set<Symbol> GetFirst(Symbol s)
        {
            Calculate();
            return m_firstSets.GetSet(s);
        }

        private void Calculate()
        {
            if (m_firstSets == null)
            {
                m_firstSets = new SetMap<Symbol, Symbol>();
                foreach (Symbol terminalSymbol in Grammar.SymbolSets.TerminalSymbols)
                {
                    m_firstSets.MakeSet(terminalSymbol).Add(terminalSymbol);
                }

                foreach (Symbol nonTerminalSymbol in Grammar.SymbolSets.NonTerminalSymbols)
                {
                    m_firstSets.MakeSet(nonTerminalSymbol);
                }

                WorkList<Production> productionList = new WorkList<Production>(true);
                foreach (Production p0 in Grammar.Productions)
                {
                    productionList.Enqueue(p0);
                }

                Production p;
                while (productionList.Dequeue(out p))
                {
                    foreach (Symbol s in p.RHS)
                    {
                        if (m_firstSets.MakeSet(p.LHS).Add(m_firstSets.MakeSet(s)))
                        {
                            productionList.Enqueue(Grammar.ProductionSets.GetProductionsByRHS(p.LHS));
                        }

                        if (!Grammar.SymbolSets.NullableSymbols.Contains(s))
                        {
                            break;
                        }
                    }
                }
/*
                bool added = true;
                while (added)
                {
                    foreach (Production p in Grammar.Productions)
                    {
                        foreach (Symbol s in p.RHS)
                        {
                            added = m_firstSets.MakeSet(p.LHS).Add(m_firstSets.MakeSet(s));

                            if (!Grammar.SymbolSets.NullableSymbols.Contains(s))
                            {
                                break;
                            }
                        }
                    }
                }
 */
            }
        }
    }
}